﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.Common;
using System.Reflection;
using System.Text;

namespace SharpSoft.Data
{
    public static class SqlUtility
    {
        /// <summary>
        /// 将object转换为指定的目标类型，若不支持转换则会报错。
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static T ToObject<T>(this object obj)
        {
            return ConvertToTargetType<T>(obj);
        }

        /// <summary>
        /// 将数据库查询出的数据转换为指定的强类型
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static T ConvertToTargetType<T>(object obj)
        {
            Type t = typeof(T);
            if (obj == null || obj == DBNull.Value)
            {//为空，返回该类型的默认值
                return default(T);
            }
            return (T)ConvertToTargetType(obj, t);
        }
        /// <summary>
        /// 尝试将对象转换为目标类型
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="targetType"></param>
        /// <returns></returns>
        public static object ConvertToTargetType(object obj, Type targetType)
        {
            if (targetType == null)
            {
                throw new Exception("必须指定目标类型。");
            }
            Type t = targetType;
            if (obj == null || obj == DBNull.Value)
            {//为空，返回该类型的默认值
                return targetType.IsValueType ? Activator.CreateInstance(targetType) : null;
            }
            if (obj.GetType() == t)
            {//与目标类型相同，直接返回原对象
                return obj;
            }
            if (targetType == typeof(object))
            {//装箱 无需转换
                return obj;
            }

            if ((t.IsConstructedGenericType && typeof(Nullable<>).Equals(t.GetGenericTypeDefinition())))
            {//如果是Nullable泛型则取泛型参数
                var types = t.GenericTypeArguments;
                if (types.Length == 1)
                {
                    t = types[0];
                }
                else
                {
                    throw new Exception("无法处理带有多个类型参数的泛型。");
                }

            }
            if (t.IsEnum)
            {//枚举类型
                if (obj is string str)
                {//如果源类型是字符串则解析枚举对象名称
                    return Enum.Parse(t, str);
                }
                else
                {//直接将数值转为枚举对象
                    return Enum.ToObject(t, obj);
                }
            }

            if (obj is IConvertible convertible)
            {//对象实现了IConvertible接口,首先尝试使用IConvertible转换
                try
                {
                    var result = Convert.ChangeType(obj, t);
                    return result;
                }
                catch (Exception)
                {
                }
            }
            if (targetType == typeof(string))
            {//转为字符串
                return Convert.ToString(obj);
            }
            var souType = obj.GetType();
            TypeConverter typeConverter = TypeDescriptor.GetConverter(t);
            if (typeConverter != null && typeConverter.CanConvertFrom(souType))
            {//目标类型定义了类型转换器且支持从源类型转换
                return typeConverter.ConvertFrom(obj);
            }
            typeConverter = TypeDescriptor.GetConverter(souType);
            if (typeConverter != null && typeConverter.CanConvertTo(t))
            {//源类型定义了类型转换器且支持转换到目标类型
                return typeConverter.ConvertTo(obj, t);
            }
            /*TODO:
             * 其他依赖于特定环境的类型转换
             * 如WPF的IValueConverter
             * */
            var pmethod = t.GetMethod("Parse", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);
            if (pmethod != null)
            {//目标类型存在静态的Parse方法,则尝试使用Parse方法将值的字符串形式解析为目标类型
             //适用于DateTime,Guid等常见的多形态值类型
                return pmethod.Invoke(null, new object[] { obj.ToString() });
            }
            throw new Exception($"暂不支持的转换：{obj.GetType().FullName}->{t.FullName}");
        }

        /// <summary>
        /// 将运行时值转换成sql语句中使用的表达形式
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static string ConvertToTSqlText(object value)
        {
            if (value == null || value is DBNull)
                return "NULL";
            Type t = value.GetType();
            if (t.IsEnum)
                return Convert.ToInt64(value).ToString();
            string result = null;
            if (t == typeof(byte[]))//字节组
            {
                byte[] data = (byte[])value;
                return "0x" + HexHelper.BytesToHexString(data);
            }
            else if (t != typeof(string) && value is IEnumerable enu)
            {//除字符串之外的可枚举类型
                List<string> strs = new List<string>();
                foreach (var item in enu)
                {
                    strs.Add(ConvertToTSqlText(item));
                }
                return $"({string.Join(',', strs)})";
            }

            TypeCode typeCode = Type.GetTypeCode(t);
            switch (typeCode)
            {
                case TypeCode.DBNull:
                    return "NULL";
                case TypeCode.Empty:
                case TypeCode.Object:
                case TypeCode.String:
                case TypeCode.Char:
                    result = Convert.ToString(value).Replace("'", "''");
                    break;
                case TypeCode.Boolean:
                    return (bool)value ? "1" : "0";
                case TypeCode.SByte:
                case TypeCode.Int16:
                case TypeCode.UInt16:
                case TypeCode.Int32:
                case TypeCode.UInt32:
                case TypeCode.Int64:
                case TypeCode.UInt64:
                case TypeCode.Single:
                case TypeCode.Double:
                case TypeCode.Decimal:
                    return Convert.ToString(value);
                case TypeCode.DateTime:
                    var dt = (DateTime)value;
                    result = dt.ToString("yyyy-MM-dd HH:mm:ss");
                    break;
                default:
                    throw new Exception("转换StringSqlValue时遇到无法识别的类型：" + typeCode.ToString());


            }
            return $"'{result}'";
        }

        /// <summary>
        /// .NET类型=>DbType<see cref="http://msdn.microsoft.com/en-us/library/cc716729.aspx"/>
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        /// <remarks>
        /// <code>
        /// typeMap = new Dictionary<Type, DbType>();
        ///typeMap[typeof(byte)] = DbType.Byte;
        ///typeMap[typeof(sbyte)] = DbType.SByte;
        ///typeMap[typeof(short)] = DbType.Int16;
        ///typeMap[typeof(ushort)] = DbType.UInt16;
        ///typeMap[typeof(int)] = DbType.Int32;
        ///typeMap[typeof(uint)] = DbType.UInt32;
        ///typeMap[typeof(long)] = DbType.Int64;
        ///typeMap[typeof(ulong)] = DbType.UInt64;
        ///typeMap[typeof(float)] = DbType.Single;
        ///typeMap[typeof(double)] = DbType.Double;
        ///typeMap[typeof(decimal)] = DbType.Decimal;
        ///typeMap[typeof(bool)] = DbType.Boolean;
        ///typeMap[typeof(string)] = DbType.String;
        ///typeMap[typeof(char)] = DbType.StringFixedLength;
        ///typeMap[typeof(Guid)] = DbType.Guid;
        ///typeMap[typeof(DateTime)] = DbType.DateTime;
        ///typeMap[typeof(DateTimeOffset)] = DbType.DateTimeOffset;
        ///typeMap[typeof(byte[])] = DbType.Binary;
        ///typeMap[typeof(byte?)] = DbType.Byte;
        ///typeMap[typeof(sbyte?)] = DbType.SByte;
        ///typeMap[typeof(short?)] = DbType.Int16;
        ///typeMap[typeof(ushort?)] = DbType.UInt16;
        ///typeMap[typeof(int?)] = DbType.Int32;
        ///typeMap[typeof(uint?)] = DbType.UInt32;
        ///typeMap[typeof(long?)] = DbType.Int64;
        ///typeMap[typeof(ulong?)] = DbType.UInt64;
        ///typeMap[typeof(float?)] = DbType.Single;
        ///typeMap[typeof(double?)] = DbType.Double;
        ///typeMap[typeof(decimal?)] = DbType.Decimal;
        ///typeMap[typeof(bool?)] = DbType.Boolean;
        ///typeMap[typeof(char?)] = DbType.StringFixedLength;
        ///typeMap[typeof(Guid?)] = DbType.Guid;
        ///typeMap[typeof(DateTime?)] = DbType.DateTime;
        ///typeMap[typeof(DateTimeOffset?)] = DbType.DateTimeOffset;
        ///typeMap[typeof(System.Data.Linq.Binary)] = DbType.Binary;
        /// </code>
        /// </remarks>
        public static DbType CLRTypeToDbType(Type type)
        {
            DbType dbType;
            switch (Type.GetTypeCode(type))
            {
                case TypeCode.String:
                    dbType = DbType.String;
                    break;
                case TypeCode.Int32:
                    dbType = DbType.Int32;
                    break;
                case TypeCode.DateTime:
                    dbType = DbType.DateTime;
                    break;
                case TypeCode.Boolean:
                    dbType = DbType.Boolean;
                    break;
                default:
                    dbType = (DbType)Enum.Parse(typeof(DbType), Type.GetTypeCode(type).ToString());
                    break;
            }

            return dbType;
        }
        /// <summary>
        /// DbType=>.NET类型
        /// </summary>
        /// <param name="dbtype"></param>
        /// <returns></returns>
        public static Type DbTypeToCLRType(DbType dbtype)
        {
            Type toReturn = typeof(DBNull);

            switch (dbtype)
            {
                case DbType.UInt64:
                    toReturn = typeof(UInt64);
                    break;
                case DbType.Int64:
                    toReturn = typeof(Int64);
                    break;
                case DbType.Int32:
                    toReturn = typeof(Int32);
                    break;
                case DbType.UInt32:
                    toReturn = typeof(UInt32);
                    break;
                case DbType.Single:
                    toReturn = typeof(float);
                    break;
                case DbType.Date:
                case DbType.DateTime:
                case DbType.Time:
                    toReturn = typeof(DateTime);
                    break;
                case DbType.String:
                case DbType.StringFixedLength:
                case DbType.AnsiString:
                case DbType.AnsiStringFixedLength:
                    toReturn = typeof(string);
                    break;
                case DbType.UInt16:
                    toReturn = typeof(UInt16);
                    break;
                case DbType.Int16:
                    toReturn = typeof(Int16);
                    break;
                case DbType.SByte:
                    toReturn = typeof(byte);
                    break;
                case DbType.Object:
                    toReturn = typeof(object);
                    break;
                case DbType.VarNumeric:
                case DbType.Decimal:
                    toReturn = typeof(decimal);
                    break;
                case DbType.Currency:
                    toReturn = typeof(double);
                    break;
                case DbType.Binary:
                    toReturn = typeof(byte[]);
                    break;
                case DbType.Double:
                    toReturn = typeof(Double);
                    break;
                case DbType.Guid:
                    toReturn = typeof(Guid);
                    break;
                case DbType.Boolean:
                    toReturn = typeof(bool);
                    break;
            }
            return toReturn;
        }


    }
}
